package com.dxy.netty.protobufserialize;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;

/**
 * @Description netty服务器 （注意它添加的protobuf解码器 ProtobufDecoder ）
 * <p>
 * 参考：netty客户端与服务器使用protobuf传输报文
 * https://blog.csdn.net/PacosonSWJTU/article/details/126692946
 * @Author dangxianyue
 * @Date 2023/1/8 15:27
 */
public class ProtobufSerializeNettyServer {

    private final int port;

    public ProtobufSerializeNettyServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) {
        try {
            new ProtobufSerializeNettyServer(6668).run();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void run() throws InterruptedException {
        // 创建 BossGroup 和 WorkerGroup
        // 1. 创建2个线程组 bossGroup， workerGroup
        // 2 bossGroup 仅处理连接请求； 真正的业务逻辑，交给workerGroup完成；
        // 3 两个线程组都是无限循环
        // 4 bossGroup 和 workerGroup 含有的子线程（NIOEventLoop）个数
        // 默认是 cpu核数 * 2
        EventLoopGroup boosGroup = new NioEventLoopGroup();
        EventLoopGroup workerGruop = new NioEventLoopGroup();

        try {
            // 创建服务器端的启动对象， 配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boosGroup, workerGruop) // 设置2个线程组
                    .channel(NioServerSocketChannel.class) // 使用NIOSocketChannel 作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列等待连接的个数
                    .childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 创建一个通道初始化对象
                        // 给 pipeline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            // 添加 protobuf 解码器, 指定对哪种对象进行解码
                            pipeline.addLast("decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
                            // 添加业务处理器
//                            pipeline.addLast(new ProtobufNettyServerHandler());
                            pipeline.addLast(new ProtobufSerializeNettyServerHandler());
                        }
                    });  // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器
            System.out.println("... server is ready.");

            // 启动服务器， 绑定端口并同步处理 ，生成一个 ChannelFuture对象
            ChannelFuture channelFuture = bootstrap.bind(this.port).sync();
            channelFuture.addListener((future1) -> System.out.println("Finish binding"));

            // 给 channelFuture 注册监听器，监听我们关心的事件
            channelFuture.addListener(future -> {
                if (future.isSuccess()) {
                    System.out.println("监听端口6668 成功");
                } else {
                    System.out.println("监听端口6668 失败");
                }
            });

            // 对关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 优雅关闭
            boosGroup.shutdownGracefully();
            workerGruop.shutdownGracefully();
        }
    }
}
